﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Graphix.Elements;
using Graphix.Helpers;

namespace Graphix.Charts
{
    using System.Globalization;
    using CommonHelpers;
    using Graphix.Charts.Base;

    public class BarChart : ColumnChart
    {
        protected override void AddBars()
        {
            float left = this.Border.Left + 1;
            float length = this.Border.Width - 1;
            int groups = this.SetsName.Count;
            float height = this.Border.Height / groups;

            float barheight = this.Border.Height / ((1 + (float)Math.Sqrt(5)) / 2);
            barheight /= groups;

            float modifier = 1;
            float position;

            if (this.XAxis.OppositeDirection)
            {
                modifier = -1;
            }

            if (!this.IsStacked)
            {
                barheight /= this.BarSet.Count;
            }

            if (this.YAxis.OppositeDirection)
            {
                if (this.XAxis.OppositeDirection)
                {
                    if (this.IsStacked)
                    {
                        position = this.Border.Bottom - barheight - ((height - barheight) / 2f); // 1
                    }
                    else
                    {
                        position = this.Border.Bottom - barheight - ((height - (barheight * this.BarSet.Count)) / 2f) + (this.BarSet.Count - 1); // 5
                    }
                }
                else
                {
                    if (!this.IsStacked)
                    {

                        var temp = barheight * this.BarSet.Count;
                        position = this.Border.Bottom - ((height - temp) + temp) + barheight; //7
                    }
                    else
                    {
                        position = this.Border.Bottom - barheight - ((height - barheight) / 2f); //3
                    }
                }
            }
            else
            {
                if (this.XAxis.OppositeDirection)
                {
                    if (this.IsStacked)
                    {
                        position = this.Border.Top + ((height - barheight) / 2f); // 2
                    }
                    else
                    {
                        position = this.Border.Top + ((barheight + 1) * this.BarSet.Count) + 1; //6 //todo better?
                    }
                }
                else
                {
                    if (this.IsStacked)
                    {
                        position = this.Border.Top + ((height - barheight) / 2f); // 4
                    }
                    else
                    {
                        position = this.Border.Top - this.BarSet.Count / 2f + ((height - (barheight * this.BarSet.Count)) / 2f); //8
                    }
                }
            }

            float space = 1;

            if (this.IsStacked)
            {
                float[] positionY = new float[this.BarSet[0].Values.Count];
                // 4
                for (int i = 0; i < this.BarSet.Count; i++)
                {
                    ValueSet<float> information = this.BarSet[i];
                    // 8
                    for (int j = 0; j < information.Values.Count; j++)
                    {
                        float value = information.Values[j];
                        float percent = value / this.XAxis.Scale.NiceMax;
                        float width = length * percent;

                        float temp;
                        float temppos;
                        if (YAxis.OppositeDirection)
                        {
                            temppos = position - (height * j);
                        }
                        else
                        {
                            temppos = position + (height * j);
                        }

                        if (XAxis.OppositeDirection)
                        {
                            temp = this.Border.Right - width;
                        }
                        else
                        {
                            temp = left;
                        }

                        var element = new Rect(temp + positionY[j], temppos, width,
                            barheight,
                            information.Color);

                        element.Attributes["g"] = i.ToString(CultureInfo.InvariantCulture);
                        string legend = this.SetsName[j];
                        if (string.IsNullOrEmpty(legend))
                        {
                            legend = " ";
                        }
                        element.Attributes["data-ot"] =
                            string.Format(CultureInfo.InvariantCulture,
                                          "l={0};v={{\"{1}\": \"{2}\"}}",
                                          legend, information.Name, value);
                        this.AddElement(element);

                        positionY[j] += (width + space + 1) * modifier;
                    }
                }
            }
            else
            {
                // 4
                for (int i = 0; i < this.BarSet.Count; i++)
                {
                    ValueSet<float> information = this.BarSet[i];
                    // 8
                    for (int j = 0; j < information.Values.Count; j++)
                    {
                        float value = information.Values[j];
                        float percent = value / this.XAxis.Scale.NiceMax;

                        float temp;
                        float temppos;
                        float width = length * percent;
                        if (YAxis.OppositeDirection)
                        {
                            temppos = position - (height * j);
                        }
                        else
                        {
                            temppos = position + (height * j);
                        }

                        if (XAxis.OppositeDirection)
                        {
                            temp = this.Border.Right - width;
                        }
                        else
                        {
                            temp = left;
                        }
                        var element = new Rect(temp, temppos, width,
                            barheight,
                            information.Color);
                        element.Attributes["g"] = i.ToString(CultureInfo.InvariantCulture);
                        string legend = this.SetsName[j];
                        if (string.IsNullOrEmpty(legend))
                        {
                            legend = " ";
                        }
                        element.Attributes["data-ot"] =
                            string.Format(CultureInfo.InvariantCulture,
                                          "l={0};v={{\"{1}\": \"{2}\"}}",
                                          legend, information.Name, value);
                        this.AddElement(element);
                    }

                    position += (barheight + space + 1) * modifier;
                }
            }
        }

        protected override void AddXAxisTicks()
        {
            if (BarSet.Count > 0)
            {
                this.XAxis.CalculateScale();

                int loop = (int)((this.XAxis.Scale.NiceMax - this.XAxis.Scale.NiceMin) / this.XAxis.Scale.TickSpacing);
                float left = this.Border.Left;
                float step = this.Border.Width / loop;
                float top = this.Border.Top;
                float bottom = this.Border.Bottom;

                for (int i = 0; i <= loop; i++)
                {
                    float position = left + (step * i);

                    Color color = i == 0 ? Color.Black : Color.LightGray;

                    float value;
                    if (this.XAxis.OppositeDirection)
                    {
                        value = this.XAxis.Scale.TickSpacing * (loop - i);
                    }
                    else
                    {
                        value = this.XAxis.Scale.TickSpacing * i;
                    }

                    string text = string.Format(this.XAxis.Format, value);

                    // todo: line color property
                    this.AddElement(new Line(position, top, position, bottom, color));

                    if (this.XAxis.DisplayValues)
                    {
                        this.AddElement(new ChartString(position, bottom, 0, 0, text,
                            this.XAxis.TextStyle.Font, this.XAxis.TextStyle.ColorBrush)
                            { Format = new StringFormat() { Alignment = StringAlignment.Center } });
                    }
                }
            }
        }

        protected override void AddYAxisTicks()
        {
            if (this.YAxis.DisplayValues)
            {
                string longest = this.SetsName.OrderByDescending(x => x.Length).First();
                RectangleF measured = DefaultGraphics.Instance.MeasureDisplayString(longest, this.XAxis.TextStyle.Font);
                this.Border.Left += measured.Width;

                int groups = this.SetsName.Count;
                float height = this.Border.Height / groups;
                float barheight = this.Border.Height / ((1 + (float)Math.Sqrt(5)) / 2);
                barheight /= groups;

                float y = barheight + (barheight / 2f);
                if (this.IsStacked)
                {
                    y -= this.BarSet.Count - 1;
                }

                float modifier = 1;

                if (this.YAxis.OppositeDirection)
                {
                    if (this.XAxis.OppositeDirection)
                    {
                        if (this.IsStacked)
                        {
                            y = this.Border.Bottom - (height / 2f); // 1
                        }
                        else
                        {
                            y = this.Border.Bottom - (height / 2f) - this.BarSet.Count + this.BarSet.Count; //5
                        }
                    }
                    else
                    {
                        if (this.IsStacked)
                        {
                            y = this.Border.Bottom - (height / 2f);
                        }
                        else
                        {
                            y = this.Border.Bottom - (height / 2f) - this.BarSet.Count + this.BarSet.Count; //7
                        }
                    }
                    modifier = -1;
                }
                else
                {
                    if (this.XAxis.OppositeDirection)
                    {
                        if (this.IsStacked)
                        {
                            y = this.Border.Top + ((height - barheight) / 2f) + barheight/2f; // 2
                        }
                        else
                        {
                            y = this.Border.Top + ((height - barheight) / 2f) + barheight / 2f
                                + this.BarSet.Count / 2f; //6
                        }
                    }
                    else
                    {
                        if (this.IsStacked)
                        {
                            y = this.Border.Top + ((height - barheight) / 2f) + barheight / 2f; // 4
                        }
                        else
                        {
                            y = this.Border.Top + ((height - barheight) / 2f) + barheight / 2f
                                + this.BarSet.Count / 2f; //8
                        }
                    }
                }

                for (int i = 0; i < this.SetsName.Count; i++)
                {
                    string text = string.Format(this.YAxis.Format, this.SetsName[i]);

                    ChartString cs = new ChartString(this.Border.Left,
                        y, 0, 0, text,
                        this.YAxis.TextStyle.Font, this.YAxis.TextStyle.ColorBrush);

                    cs.Format = new StringFormat()
                        {
                            Alignment = StringAlignment.Far,
                            LineAlignment = StringAlignment.Center
                        };
                    cs.MoveWhen = PerformMove.Right | PerformMove.Bottom | PerformMove.Top;

                    this.AddElement(cs);

                    y += height * modifier;
                }
            }
        }

        protected override void AddTicks()
        {
            // this.MoveBeforeXAxisTicks();
            if (this.XAxis.DisplayValues)
            {
                this.Border.Bottom -= DefaultGraphics.Instance.MeasureDisplayString("|", this.XAxis.TextStyle.Font).Height;
            }

            this.AddYAxisTicks();
            this.AddXAxisTicks();
        }

        private void MoveBeforeXAxisTicks()
        {
            string longest = this.SetsName.OrderByDescending(x => x.Length).First();
            RectangleF measured = DefaultGraphics.Instance.MeasureDisplayString(longest, this.XAxis.TextStyle.Font);
            this.Border.Left += measured.Width;
        }
    }
}
